using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Security;

using Impact.Infrastructure.Configuration;
using Impact.Model.Objects;
using Impact.Controllers;

using StructureMap;

namespace Impact.Web.Foundation.MetaWeblog
{
    /// <summary>
    /// HTTP Handler for MetaWeblog API
    /// </summary>
    public class MetaWeblogHandler : IHttpHandler, IMetaWeblogAPI
    {
        private HttpContext _request;

        #region IHttpHandler Members

        /// <summary>
        /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"></see> instance.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Web.IHttpHandler"></see> instance is reusable; otherwise, false.</returns>
        public bool IsReusable
        {
            get { return false; }
        }

        /// <summary>
        /// Process the HTTP Request.  Create XMLRPC request, find method call, process it and create response object and sent it back.
        /// This is the heart of the MetaWeblog API
        /// </summary>
        /// <param name="context"></param>
        public void ProcessRequest( HttpContext context )
        {
            _request = context;

            try
            {
                XMLRPCRequest input = new XMLRPCRequest( context );
                XMLRPCResponse output = new XMLRPCResponse( input.MethodName );

                switch ( input.MethodName )
                {
                    case "metaWeblog.newPost":
                        output.PostID = NewPost( input.BlogID, input.UserName, input.Password, input.Post, input.Publish );
                        break;
                    case "metaWeblog.editPost":
                        output.Completed = EditPost( input.PostID, input.UserName, input.Password, input.Post, input.Publish );
                        break;
                    case "metaWeblog.getPost":
                        output.Post = GetPost( input.PostID, input.UserName, input.Password );
                        break;
                    case "metaWeblog.newMediaObject":
                        output.MediaInfo = NewMediaObject( input.BlogID, input.UserName, input.Password, input.MediaObject );
                        break;
                    case "metaWeblog.getCategories":
                        output.Categories = GetCategories( input.BlogID, input.UserName, input.Password );
                        break;
                    case "metaWeblog.getRecentPosts":
                        output.Posts = GetRecentPosts( input.BlogID, input.UserName, input.Password, input.NumberOfPosts );
                        break;
                    case "blogger.getUsersBlogs":
                    case "metaWeblog.getUsersBlogs":
                        output.Blogs = GetUserBlogs( input.AppKey, input.UserName, input.Password );
                        break;
                    case "blogger.deletePost":
                        output.Completed = DeletePost( input.AppKey, input.PostID, input.UserName, input.Password, input.Publish );
                        break;
                    case "blogger.getUserInfo":
                        //TODO: Implement getUserInfo
                        throw new MetaWeblogException( "10", "The method GetUserInfo is not implemented." );

                }
                output.Response( context );
            }
            catch ( MetaWeblogException mex )
            {
                XMLRPCResponse output = new XMLRPCResponse( "fault" );
                MWAFault fault = new MWAFault();
                fault.faultCode = mex.Code;
                fault.faultString = mex.Message;
                output.Fault = fault;
                output.Response( context );
            }
            catch ( Exception ex )
            {
                XMLRPCResponse output = new XMLRPCResponse( "fault" );
                MWAFault fault = new MWAFault();
                fault.faultCode = "0";
                fault.faultString = ex.Message;
                output.Fault = fault;
                output.Response( context );
            }
        }

        #endregion

        #region IMetaWeblogAPI Members

        /// <summary>
        /// metaWeblog.newPost
        /// </summary>
        /// <param name="blogID">not used</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <param name="sentPost">struct with post details</param>
        /// <param name="publish">mark as published?</param>
        /// <returns>postID as string</returns>
        public string NewPost( string blogID, string userName, string password, MWAPost sentPost, bool publish )
        {
            Guid userId = ValidateRequest( userName, password );

            Article article = new Article();

            article.Title = sentPost.title;
            article.Body = sentPost.description;
            article.Enabled = publish;

            foreach ( string item in sentPost.categories )
            {
                ArticleCategory category = new ArticleCategory();
                category.LoadByParam( ArticleCategory.Columns.NameKey, item );
                if ( ArticleCategory.IsValid( category ) )
                {
                    article.CategoryId = category.CategoryId;
                    break;
                }
            }

            article.Save(userId);

            return article.ArticleId.ToString();
        }

        /// <summary>
        /// metaWeblog.editPost
        /// </summary>
        /// <param name="postID">article ID in string format</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <param name="sentPost">struct with post details</param>
        /// <param name="publish">mark as published?</param>
        /// <returns>1 if successful</returns>
        public bool EditPost( string postID, string userName, string password, MWAPost sentPost, bool publish )
        {
            Guid userId = ValidateRequest( userName, password );

            int articleId = Int32.Parse( postID );
            Article article = Article.FetchByID( articleId );

            article.Title = sentPost.title;
            article.Body = sentPost.description;
            article.Enabled = publish;

            foreach ( string item in sentPost.categories )
            {
                ArticleCategory category = new ArticleCategory();
                category.LoadByParam( ArticleCategory.Columns.NameKey, item );
                if ( ArticleCategory.IsValid( category ) )
                {
                    article.CategoryId = category.CategoryId;
                    break;
                }
            }

            article.Save(userId);

            return true;
        }

        /// <summary>
        /// metaWeblog.getPost
        /// </summary>
        /// <param name="postID">post guid in string format</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <returns>struct with post details</returns>
        public MWAPost GetPost( string postID, string userName, string password )
        {
            ValidateRequest( userName, password );

            int articleId = Int32.Parse( postID );
            Article article = Article.FetchByID( articleId );

            return ArticleToPost( article );
        }

        /// <summary>
        /// metaWeblog.newMediaObject
        /// </summary>
        /// <param name="blogID">not used</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <param name="mediaObject">struct with media details</param>
        /// <returns>struct with url to media</returns>
        public MWAMediaInfo NewMediaObject( string blogID, string userName, string password, MWAMediaObject mediaObject )
        {
            /*
            ValidateRequest( userName, password );

            MWAMediaInfo mediaInfo = new MWAMediaInfo();

            string rootPath = BlogSettings.Instance.StorageLocation + "files/";
            string serverPath = _request.Server.MapPath( rootPath );
            string saveFolder = serverPath;
            string fileName = mediaObject.name;

            // Check/Create Folders & Fix fileName
            if ( mediaObject.name.LastIndexOf( '/' ) > -1 )
            {
                saveFolder += mediaObject.name.Substring( 0, mediaObject.name.LastIndexOf( '/' ) );
                saveFolder = saveFolder.Replace( '/', '\\' );


                fileName = mediaObject.name.Substring( mediaObject.name.LastIndexOf( '/' ) + 1 );
            }
            else
            {
                if ( saveFolder.EndsWith( "\\" ) )
                    saveFolder = saveFolder.Substring( 0, saveFolder.Length - 1 );
            }
            if ( !Directory.Exists( saveFolder ) )
                Directory.CreateDirectory( saveFolder );
            saveFolder += "\\";

            // Save File
            FileStream fs = new FileStream( saveFolder + fileName, FileMode.Create );
            BinaryWriter bw = new BinaryWriter( fs );
            bw.Write( mediaObject.bits );

            // Set Url
            string rootUrl = _request.Request.Url.ToString().Substring( 0, _request.Request.Url.ToString().IndexOf( "metaweblog.axd" ) );

            string mediaType = mediaObject.type;
            if ( mediaType.IndexOf( '/' ) > -1 )
                mediaType = mediaType.Substring( 0, mediaType.IndexOf( '/' ) );
            switch ( mediaType )
            {
                case "image":
                case "notsent": // If there wasn't a type, let's pretend it is an image.  (Thanks Zoundry.  This is for you.)
                    rootUrl += "image.axd?picture=";
                    break;
                default:
                    rootUrl += "file.axd?file=";
                    break;
            }

            mediaInfo.url = rootUrl + mediaObject.name;
            return mediaInfo;
             * */

            throw new NotImplementedException();
        }

        /// <summary>
        /// metaWeblog.getCategories
        /// </summary>
        /// <param name="blogID">always 1000 in BlogEngine since it is a singlar blog instance</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <returns>array of category structs</returns>
        public List<MWACategory> GetCategories( string blogID, string userName, string password )
        {
            List<MWACategory> categories = new List<MWACategory>();

            ValidateRequest( userName, password );

            string rootUrl = _request.Request.Url.ToString().Substring( 0, _request.Request.Url.ToString().IndexOf( "metaweblog.axd" ) );

            ArticleCategoryCollection articleCategories = new ArticleCategoryCollection().Load();

            foreach ( ArticleCategory category in articleCategories )
            {
                MWACategory temp = new MWACategory();
                temp.description = category.Name;
                temp.htmlUrl = ContentPresenter.FormatUrl( category );
                if(category.AllowRss)
                {

                    // http://localhost/Impact/!rss.ashx?c=PressReleases

                    temp.rssUrl = VirtualPathUtility.Combine( VirtualPathUtility.AppendTrailingSlash( HttpRuntime.AppDomainAppVirtualPath ),
                        string.Format( "!rss.ashx?c={0}", category.NameKey ) );
                }
                categories.Add( temp );
            }

            return categories;
        }

        /// <summary>
        /// metaWeblog.getRecentPosts
        /// </summary>
        /// <param name="blogID">not used</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <param name="numberOfPosts">number of posts to return</param>
        /// <returns>array of post structs</returns>
        public List<MWAPost> GetRecentPosts( string blogID, string userName, string password, int numberOfPosts )
        {
            ValidateRequest( userName, password );

            List<MWAPost> sendPosts = new List<MWAPost>();
            ArticleCollection posts = new ArticleCollection();

            SubSonic.Query query = Article.CreateQuery();
            query.Top = numberOfPosts.ToString();
            posts.LoadAndCloseReader( Article.FetchByQuery( query ) );

            foreach ( Article post in posts )
            {
                MWAPost tempPost = ArticleToPost( post );
                sendPosts.Add( tempPost );
            }

            return sendPosts;
        }

        /// <summary>
        /// blogger.getUsersBlogs
        /// </summary>
        /// <param name="appKey">Key from application.  Outdated methodology that has no use here.</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <returns>array of blog structs</returns>
        public List<MWABlogInfo> GetUserBlogs( string appKey, string userName, string password )
        {
            List<MWABlogInfo> blogs = new List<MWABlogInfo>();

            ValidateRequest( userName, password );

            MWABlogInfo temp = new MWABlogInfo();
            temp.url = VirtualPathUtility.Combine( VirtualPathUtility.AppendTrailingSlash( HttpRuntime.AppDomainAppVirtualPath ),
                "!rss.ashx" );
            //temp.blogID = "1000";

            IConfigProvider config = ObjectFactory.GetInstance<IConfigProvider>();
            temp.blogName = config.Settings.RssDescription;
            blogs.Add( temp );

            return blogs;
        }

        /// <summary>
        /// blogger.deletePost
        /// </summary>
        /// <param name="appKey">Key from application.  Outdated methodology that has no use here.</param>
        /// <param name="postID">post guid in string format</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <param name="publish">mark as published?</param>
        /// <returns></returns>
        public bool DeletePost( string appKey, string postID, string userName, string password, bool publish )
        {
            ValidateRequest( userName, password );
            try
            {
                int articleId = Int32.Parse( postID );
                Article.Delete( articleId );
            }
            catch ( Exception ex )
            {
                throw new MetaWeblogException( "12", "DeletePost failed.  Error: " + ex.Message );
            }

            return true;
        }

        /// <summary>
        /// blogger.getUserInfo
        /// </summary>
        /// <remarks>
        /// This is never called in anything I've tested.
        /// </remarks>
        /// <param name="appKey">Key from application.  Outdated methodology that has no use here.</param>
        /// <param name="userName">login username</param>
        /// <param name="password">login password</param>
        /// <returns>struct with user data</returns>
        public MWAUserInfo GetUserInfo( string appKey, string userName, string password )
        {
            throw new MetaWeblogException( "10", "The method GetUserInfo is not implemented." );
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Checks username and password.  Throws error if validation fails.
        /// </summary>
        /// <param name="userName"></param>
        /// <param name="password"></param>
        private Guid ValidateRequest( string userName, string password )
        {
            Guid userId = Guid.Empty;
            if ( !Membership.ValidateUser( userName, password ) )
            {
                throw new MetaWeblogException( "11", "User authentication failed" );
            }
            else
            {
                MembershipUser user = Membership.GetUser( userName );
                if ( user == null )
                {
                    throw new MetaWeblogException( "22", string.Format( "Cannot find user: {0}", userName ) );
                }
                else
                {
                    userId = (Guid)user.ProviderUserKey;
                }
            }
            return userId;
        }

        private MWAPost ArticleToPost(Article article)
        {
            MWAPost post = new MWAPost();

            post.postID = article.ArticleId.ToString();
            post.postDate = article.ModifiedOn ?? article.CreatedOn;
            post.title = article.Title;
            post.description = article.Body;
            post.link = ContentPresenter.FormatUrl( article );
            post.publish = article.Enabled;

            List<string> cats = new List<string>();
            cats.Add( article.CategoryId.ToString() );
            post.categories = cats;

            return post;
        }

        #endregion
    }
}
